package com.ccl.iot.views;


import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.FontMetrics;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.view.SurfaceHolder;

import com.ccl.iot.privates.ListMap;
import com.ccl.iot.template.GraphicView;

import java.util.ArrayList;

public class CurveGraphicView extends GraphicView {
	/*
	 * Classes
	 */
	private class ColorPicker{
		private final int KCOLOR[] = {0x000000, 0xFFFFFF, 0x8080FF, 0xFFFF80, 0x80FF80, 0x80FFFF, 0x0080FF, 0xFF80C0, 0xFF80FF,
															0xFF0000, 0x0080C0, 0x8080C0, 0xFF00FF, 
															0x804040, 0xFF8040, 0x004080, 0x8080FF, 0x800040, 0xFF0080,
															0x008000, 0x0000FF, 0x800080, 0x8000FF};
		private int iLevel;
		private int iColorStep;
		private int iIndex;
		private int iCount;
		
		ColorPicker(int aLevel){
			iLevel = aLevel == 0 ? 1 : aLevel;
			
			iColorStep = 255 / iLevel;
			iCount = KCOLOR.length * iLevel;
		}
		
		public int getFirstColor(){
			iIndex = iLevel;
			
			return 0xFF000000 | KCOLOR[0];
		}
		
		public int getNextColor(){
			int tLevel;
			int tIndex;
			int tColor;
			
			tLevel = iIndex % iLevel;
			tIndex = (iIndex - tLevel) / iLevel;
			
			tColor = (((tLevel + 1) * iColorStep) << 24) | KCOLOR[tIndex];
			
			if (++iIndex == iCount){
				iIndex = 0;
			}
			
			return tColor;
		}
	}
	
	/*
	 * Non-static functions
	 */
	private float iMinValue;
	private float iMaxValue;
	private float iStepWidth;
	private float iLineWidth;
	
	private ColorPicker iColorPicker;
	
	private int iWidth;
	private int iHeight;
	private float iPerValueHeight;
	private int iHistoryDepth = 0;
	private boolean iShowGrid = true;
	
	private boolean iLockUpdate = false;
	private boolean iChanged = false;
	
	private Paint iPaint;
	private Paint iGridPaint;
	private Paint iBGPaint;
	private float iTextHeight;
	private float iTextBaseLine;
	
	private Object iSyncLock = new Object();
	private ArrayList<String> iHighLight = null;
	private ListMap<String, ArrayList<Float>> iHistory = null;
	
	public CurveGraphicView(Context aContext, float aMinValue, float aMaxValue, float aStepWidth, float aLineWidth, int aLevel) {
		super(aContext);
		
		iMinValue = aMinValue;
		iMaxValue = aMaxValue;
		iStepWidth = aStepWidth;
		iLineWidth = aLineWidth;
		
		iColorPicker = new ColorPicker(aLevel);
		
		iPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
		iPaint.setStyle(Style.STROKE);
		iPaint.setTextSize(20);
		iPaint.setAntiAlias(true);
		
		iGridPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
		iGridPaint.setStyle(Style.STROKE);
		iGridPaint.setColor(Color.LTGRAY);

		iBGPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
		iBGPaint.setStyle(Style.STROKE);
		iBGPaint.setColor(Color.TRANSPARENT);
		
		updateTextInfo();
	}
	
	public void setShowGrid(boolean aShow){
		if (iShowGrid != aShow){
			iShowGrid = aShow;
			
			super.postInvalidate();
		}
	}
	
	private void updateTextInfo(){
		if (iPaint != null){
			FontMetrics tFontMetrics;
			
			tFontMetrics = iPaint.getFontMetrics();
			iTextHeight = (float) Math.ceil(tFontMetrics.descent - tFontMetrics.ascent);
			iTextBaseLine = (iTextHeight - tFontMetrics.bottom + tFontMetrics.top) / 2 - tFontMetrics.top;
		}
	}
	
	@Override
	protected void onPaint(Canvas aCanvas) {
		super.onPaint(aCanvas);
		
		if (aCanvas != null && iHistory != null){
			int i, j, tCount, tCount1;
			Path tPath = new Path();
			
			iPaint.setStrokeWidth(iLineWidth);

			aCanvas.drawColor(iColorPicker.getFirstColor());
			iPaint.setColor(iColorPicker.getNextColor());
			aCanvas.drawRect(0, 0, iWidth, iHeight, iPaint);
			aCanvas.drawText(super.getFrameRate() + "fps", 0, iTextBaseLine, iPaint);
			
			if (iShowGrid){
				int tHeight = iHeight / 10;
				
				for (i = 0; i < iHeight; i += tHeight){
					aCanvas.drawLine((float)0, (float)i, (float)iWidth, (float)i, iGridPaint);
				}
				for (i = 0; i < iWidth; i += iStepWidth){
					aCanvas.drawLine((float)i, (float)0, (float)i, (float)iHeight, iGridPaint);
				}
			}
			
			synchronized(iSyncLock){
				for (i = 0, tCount = iHistory.size(); i < tCount; i++){
					ArrayList<Float> tHistory = iHistory.getValueAt(i);
	
					if (tHistory != null){
						float tAdditionWidth = (iHighLight != null && iHighLight.contains(iHistory.getKeyAt(i)) ? iLineWidth + 1 : 0);
						Float tLastValue = null;
						
						synchronized(tHistory){
							tCount1 = tHistory.size();
							
							if (tCount1 > 0){
								tPath.reset();
								
								iPaint.setColor(iColorPicker.getNextColor());
								
								if (tHistory.get(tCount1 - 1) == null){
									iPaint.setStrokeWidth(1);
								}else{
									iPaint.setStrokeWidth(iLineWidth);
								}
								
								aCanvas.drawText((tAdditionWidth > 0 ? ">" : "") + iHistory.getKeyAt(i) + " = " + tHistory.get(tCount1 - 1) + (tAdditionWidth > 0 ? "<" : ""), 0, iTextHeight * (i + 1) + iTextBaseLine, iPaint);
								
								for (j = 1; j < tCount1; j++){
									Float tPrevValue = tHistory.get(j - 1);
									Float tValue = tHistory.get(j);
									
									if (tValue != null && tPrevValue != null){
										tLastValue = tValue.floatValue();
										iPaint.setStrokeWidth(iLineWidth + tAdditionWidth);
										aCanvas.drawLine(iWidth - ((j - 1) * iStepWidth), (iMaxValue - tPrevValue) * iPerValueHeight, iWidth - (j * iStepWidth), (iMaxValue - tValue) * iPerValueHeight, iPaint);					
									}else{
										if (tLastValue == null){
											for (int k = j - 1; k < tCount1; k++){
												if ((tLastValue = tHistory.get(k)) != null){
													break;
												}
											}
											
											if (tLastValue == null){
												tLastValue = iMinValue;
											}
										}
										
										if (tValue != null){
											iPaint.setStrokeWidth(iLineWidth + 1 + tAdditionWidth);
											aCanvas.drawPoint(iWidth - (j * iStepWidth), (iMaxValue - tValue) * iPerValueHeight, iPaint);
											tPrevValue = tLastValue;
											tLastValue = tValue;
										}else if (tPrevValue != null){
											iPaint.setStrokeWidth(iLineWidth + 1 + tAdditionWidth);
											aCanvas.drawPoint(iWidth - ((j - 1) * iStepWidth), (iMaxValue - tPrevValue) * iPerValueHeight, iPaint);
											tValue = tPrevValue;
										}else{
											tValue = tLastValue;
											tPrevValue = tLastValue;
										}
										
										iPaint.setStrokeWidth(1);
										aCanvas.drawLine(iWidth - ((j - 1) * iStepWidth), (iMaxValue - tPrevValue) * iPerValueHeight, iWidth - (j * iStepWidth), (iMaxValue - tValue) * iPerValueHeight, iPaint);
									}
								}
							}
						}
					}
				}
			}
		}
	}
	
	@Override
	public void surfaceChanged(SurfaceHolder aHolder, int aFormat, int aWidth, int aHeight) {
		iWidth = aWidth;
		iHeight = aHeight;
		
		iHistoryDepth = (int) (aWidth / iStepWidth);
		
		iPerValueHeight = aHeight;
		iPerValueHeight /=  (iMaxValue - iMinValue);
		
		super.surfaceChanged(aHolder, aFormat, aWidth, aHeight);
	}
	
	public void lockUpdate(){
		if (iLockUpdate == false){
			iLockUpdate = true;
			
			iChanged = false;
		}
	}
	
	public void unlockUpdate(){
		if (iLockUpdate){
			iLockUpdate = false;
			
			if (iChanged){
				iChanged = false;
				
				super.postInvalidate();
			}
		}
	}

	public boolean add(String aID, Float aValue){
		ArrayList<Float> tHistory = null;
		
		if (aID == null){
			aID = "";
		}
		
		if (iHistory == null){
			iHistory = new ListMap<String, ArrayList<Float>>();
		}else{
			synchronized(iSyncLock){
				tHistory = iHistory.getValue(aID);
			}
		}
		
		if (tHistory == null){
			tHistory = new ArrayList<Float>();
			
			iHistory.add(aID, tHistory);
		}
		
		if (tHistory.add(aValue)){
			synchronized(tHistory){
				if (tHistory.size() > iHistoryDepth){
					tHistory.remove(0);
				}
			}
			
			iChanged = true;
			
			if (iLockUpdate == false){
				super.postInvalidate();
			}
			
			return true;
		}
		
		return false;
	}

	public boolean add(String aID, float aValue){
		return add(aID, Float.valueOf(aValue));
	}

	public boolean skip(String aID){
		return add(aID, null);
	}
	
	public boolean remove(String aID){
		if (iHistory != null){
			boolean tRet = false;
			
			synchronized(iSyncLock){
				tRet = iHistory.remove(aID);
			}
			
			if (tRet){
				super.postInvalidate();
				
				return true;
			}
		}
		
		return false;
	}
	
	public boolean clear(){
		boolean tRet = false;
		
		synchronized(iSyncLock){
			if (iHistory != null && iHistory.size() > 0){
				iHistory.clear();
				
				tRet = true;
			}
			
			if (iHighLight != null && iHighLight.size() > 0){
				iHighLight.clear();
				
				tRet = true;
			}
		}
		
		if (tRet){
			super.postInvalidate();
			
			return true;
		}
		
		return false;
	}
	
	public boolean setHighLight(String aID, boolean aHighLight){
		if (aID != null){
			boolean tRet = false;
			
			synchronized(iSyncLock){
				if (aHighLight){
					if (iHighLight == null){
						iHighLight = new ArrayList<String>();
					}else if (iHighLight.contains(aID)){
						return true;
					}
					
					tRet = iHighLight.add(aID);
				}else{
					if (iHighLight != null){
						tRet = iHighLight.remove(aID);
					}
				}
			}
			
			if (tRet){
				super.postInvalidate();
				
				return true;
			}
		}
		
		return false;
	}
}
